<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>分享（20170904）</title>
  <link rel="icon" type="image/png" href="../think/assets/ant.png">
  <link href="../styles.css" rel="stylesheet">
  <link href="../vs.css" rel="stylesheet">
  <!-- Global site tag (gtag.js) - Google Analytics -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=UA-131906119-1"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());

    gtag('config', 'UA-131906119-1');
  </script>
  <script async src="../index.js"></script>
</head>
<body>
<h1>分享总结（20170904）</h1>
<p><a href="../share/20170904/20170904.pdf">分享PPT</a></p>
<p>这是第一次分享，主要介绍了变量、作用域、闭包三个概念。对每个概念，分别介绍了它们的定义、引进的原因，它们之间的关系。另外一个主要的方面是从作用域视角下介绍了闭包，提出闭包是实现词法作用域的一种方式罢了，并没有什么神秘之处。</p>
<p>具体来说：</p>
<p>变量（variable）作为一个基本的概念几乎出现在所有的编程语言中。它由标识符（identifier）和值（value）组成，标识符用于指代某个值。变量的引进，带来了编程语言抽象能力的提升。变量可以代表某一类值但却不需要具体是代表多少，或者说需要代表某个具体的值时就可以代表这个值。换一种说法，这其实是一种抽象，使得我们可以给某一类值定义某种运算模式，具体执行时给定不同的值可以产生不同的结果。</p>
<p>引进变量所带来的问题是如何管理标识符到值的映射，如何处理标识符的冲突。作用域就是用来解决该问题的，它的作用就是指定变量生效的范围和根据变量获取值的方式。比如其中的词法作用域（lexical scoping）。首先该作用域规定每个变量都属于某个作用域，作用域之间可以进行嵌套，也就是所有作用域会形成树这种数据结构。代码执行时总是处在某个作用域中，并且在外成作用域中运行的代码是无法访问到内层作用域中的变量。</p>
<p>词法作用域最重要的约束是，如果某个作用域中的代码包含了自由变量（即在该作用域中不存在，需要到外层作用域中查找），那么这些自由变量应该从执行代码的作用域的外层作用域中寻找，这里的核心是这个外层作用域应该是在编写代码的时候（或者说编译阶段）确定的，或者说代码块定义的时候所处的作用域。这和动态作用域不同，动态作用域中这个外层作用域是该代码执行时的外层作用域。执行时，一段代码可能在不同的地方执行，自然在动态作用域下同一个自由变量会有所不同。</p>
<p>在深入些，但我们在写代码的时候，比如下面的代码：</p>
<pre><code class="language-js"><span class="hljs-keyword">let</span> a = <span class="hljs-number">42</span>;
<span class="hljs-keyword">let</span> f = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-number">2</span> * a);
};
{
    <span class="hljs-keyword">let</span> a = <span class="hljs-number">43</span>;
    f();
}
</code></pre>
<p>在定义函数<code>f</code>的函数体时，里面有一个自由变量<code>a</code>。对于编码的人来说，自然认为是指全局下的那个<code>a</code>，即使<code>f</code>可能在一个<code>a</code>为<code>43</code>的作用域下执行。这样来看，词法作用域更符合<strong>人的思维</strong>。</p>
<p>既然词法作用域这么好，那如何去实现呢？</p>
<p>一种实现方式是通过闭包。闭包的定义是函数加上函数定义时所处的作用域。有了这个函数定义时所处的作用域，这个函数不管在哪个作用域下运行，都能准确的找到函数体中的自由变量所代表的值是什么。所以，这就是闭包，一点都不神秘。</p>
<p>因为JS中使用了词法作用域，同时函数是一等公民，多个对函数的引用容易导致闭包所携带的外层作用域无法销毁。在编写代码时，需要注意这方面的问题，在不需要该闭包的时候应该清除所有对该闭包的引用。</p>
<h2>参考</h2>
<ul>
<li><a href="http://www.yinwang.org/blog-cn/2013/03/26/lisp-dead-alive">Lisp 已死，Lisp 万岁！</a></li>
</ul>


</body>
</html>
